/**************************************************************
*  Apple LLVM version 10.0.0 (clang-1000.10.43.1)             *
*  Copyright (C) 2021 Xuan-Yu-San 307100890@qq.com.           *
*                                                             *
*  This file is part of little-nginx.                         *
*  Little-NGINX is a castration version NGINX for learning    *
*  HTTP Message.                                              *
*                                                             *
*  @file     Server.h                                         *
*  @github   https://gitee.com/xuanyusan/little_nginx.git     *
*  @license  MIT License (MIT)                                *
**************************************************************/

#ifndef SOCKET_HPP
#define SOCKET_HPP
#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <string>

#include "Logger.hpp"
#include "NginxConfig.hpp"

#define BUFFER_SIZE 2048
#define SOCKET_TIMETOUT 10000

using namespace std;

/**
 * @author Xuan-Yu-San
 * @version 1.0.0
 * @date 20210914
 * @brief Socker Connect
 * ...
*/
class Socket {
protected:
    int socket_id; /**< Socker Connect ID */
    int socket_port; /**< Socker Connect PORT */
    string socket_host; /**< Socker Connect HOST for HTTP Client, HTTP Server bind local IP */
    struct sockaddr_in sockaddr;
public:
    Socket(int, string);
    Socket(NginxConfig);
    ~Socket();
};

Socket::Socket(int socket_port, string socket_host):socket_port(socket_port), socket_host(socket_host) {
    this->socket_id = socket(AF_INET,SOCK_STREAM,0);
    memset(&this->sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    this->sockaddr.sin_port = htons(this->socket_port);
}

Socket::Socket(NginxConfig nginx_config){
    this->socket_port = nginx_config.proxy_port;
    this->socket_host = nginx_config.proxy_host;
    this->socket_id = socket(AF_INET,SOCK_STREAM,0);
    memset(&this->sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    this->sockaddr.sin_port = htons(this->socket_port);
}

Socket::~Socket() {}

class Connect {
protected:
    int connect_id;
    string connect_ip;
    void reset_connect_id(int);
    void check_connect_id();
public:
    string getClientIP(){return this->connect_ip;}
    Connect(int, string);
    void send_message(string);
    string recv_message();
    ~Connect();
};

void Connect::reset_connect_id(int new_id) {
    this->connect_id = new_id;
    if(new_id < 0) return ;
    struct timeval timeout={SOCKET_TIMETOUT/1000,SOCKET_TIMETOUT%1000};
    int ret = setsockopt(this->connect_id, SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout));
}

void Connect::check_connect_id() {
    if(this->connect_id<0) {
        perror("Connect error");
        exit(1);
    }
}

Connect::Connect(int connect_id, string connect_ip = ""):connect_id(connect_id), connect_ip(connect_ip) {
    if(connect_id < 0) return ;
    struct timeval timeout={SOCKET_TIMETOUT/1000,SOCKET_TIMETOUT%1000};
    int ret = setsockopt(this->connect_id, SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout));
};

void Connect::send_message(string sendbuf) {
    this->check_connect_id();
    send(this->connect_id, sendbuf.c_str(), sendbuf.size(), 0);
}

string Connect::recv_message() {
    this->check_connect_id();
    char recvbuf[BUFFER_SIZE];
    int recvd = recv(this->connect_id, recvbuf, sizeof(recvbuf), 0);
    if(recvd == -1){
        Logger_error("recv timeout");
        return "";
    }
    if(recvd == 0){
        Logger_info("connect close");
        exit(1);
    }
    return recvbuf;
}

Connect::~Connect() {
    close(this->connect_id);
};
#endif